package com.baizhi.bzmall.admin.config;

import com.baizhi.bzmall.admin.service.AdminService;
import com.baizhi.bzmall.admin.service.MenuService;
import com.baizhi.bzmall.entity.BzAdmin;
import com.baizhi.bzmall.entity.Menu;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.stream.Collectors;

//@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private AdminService adminService;
    @Autowired
    private MenuService menuService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       /*
			通过adminService查询数据库，并将查询到的用户名密码封装成User对象（User是SpringSecurity内置的UserDetails实现类）
		*/
        auth.userDetailsService(new UserDetailsService() {
            @Override
            public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                BzAdmin admin = adminService.selectOne(username);
                if(admin == null){
                    throw new UsernameNotFoundException(username + "用户不存在");
                }
                List<Menu> menus = menuService.selectPermissionByUsername(username);
                Set<String> permessionSet = menus.stream().filter((menu)->menu.getPerms()!=null&&!"".equals(menu.getPerms())).map(Menu::getPerms).collect(Collectors.toSet());
                return new User(admin.getUsername(),admin.getPassword(),
                        AuthorityUtils.createAuthorityList(permessionSet.toArray(new String[0])));

            }
        }).passwordEncoder(NoOpPasswordEncoder.getInstance());
    }

    /**
     * HttpSecurity 配置拦截规则 跳转 和 自定义登录页面
     *
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /*
         * authorizition 授权
         * 在shiro和SpringSecurity中 所有以Author开头的单词都和授权业务有关系
         *
         * authorizeRequests 配置拦截规则
         * antMatchers 配置路径
         * permitAll 不拦截,无条件允许访问
         */
        http.authorizeRequests()
                // /log /register 不拦截
                .antMatchers("/**","/login","/register")
                .permitAll()
//                .antMatchers(HttpMethod.GET,"/admins").hasAnyAuthority("system:admin:select")
//                .antMatchers(HttpMethod.GET,"/roles").hasAnyAuthority("system:admin:select2")
                // 其它请求，必须要认证授权后才可以访问到
                .anyRequest()
                .authenticated();


        //配置跨域
        http.cors()
                //未登录时，设置的响应json内容
                .and().exceptionHandling()
                .authenticationEntryPoint(new AuthenticationEntryPoint() {
                    @Override
                    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                        PrintWriter out = response.getWriter();
                        Map<String,Object> map = new HashMap<String,Object>();
                        map.put("code",401);
                        map.put("message","未登录");
                        out.write(new ObjectMapper().writeValueAsString(map));
                        out.flush();
                        out.close();
                    }
                })
                .and()
                .formLogin()
                //处理登录请求的地
                .loginProcessingUrl("/login")
                //登录成功时的处理
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        response.setStatus(HttpServletResponse.SC_OK);
                        PrintWriter out = response.getWriter();
                        Map<String,Object> map = new HashMap<String,Object>();
                        map.put("code",200);
                        map.put("message","登录成功");
                        map.put("authority",authentication.getAuthorities().stream().map(auth->auth.getAuthority()).toArray());
                        out.write(new ObjectMapper().writeValueAsString(map));
                        out.flush();
                        out.close();
                    }
                })
                //登录失败时的处理
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                        PrintWriter out = response.getWriter();
                        Map<String,Object> map = new HashMap<String,Object>();
                        map.put("code",400);
                        map.put("message","登录失败");
                        out.write(new ObjectMapper().writeValueAsString(map));
                        out.flush();
                        out.close();
                    }
                })
                .and()
                //禁用csrf保护
                .csrf().disable();
    }

    @Bean
    /**
     进行跨域的配置，注意方法名固定写法，不能修改
     */
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("http://localhost:8081"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE","OPTIONS"));
        configuration.setAllowedHeaders(Arrays.asList("content-type"));
        configuration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
    @Bean
    public BCryptPasswordEncoder bcryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

}
